﻿using Microsoft.TeamFoundation.Build.Client;
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.Framework.Client;
using Microsoft.TeamFoundation.Server;
using Microsoft.TeamFoundation.VersionControl.Client;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Windows.Forms;
using System.Xml.Linq;

namespace TfsObjects
{
    public class TfsCollection
    {
        public TfsTeamProjectCollection Collection { get; set; }
        public IBuildServer BuildServer { get; set; }
        public VersionControlServer VersionControlServer { get; set; }
        //public WorkItemStore WorkItemStore { get; set; }
        public Boolean Connected { get; set; }

        public static TfsCollection ConnectTo()
        {
            using (TeamProjectPicker tpp = new TeamProjectPicker(TeamProjectPickerMode.NoProject, false))
            {
                var result = tpp.ShowDialog();
                if (result == DialogResult.OK)
                {
                    return new TfsCollection(tpp.SelectedTeamProjectCollection.Uri);
                }
            }
            return null;
        }

        public TfsCollection(Uri url)
        {
            this.Collection = new TfsTeamProjectCollection(url);
            this.BuildServer = this.Collection.GetService<IBuildServer>();
            this.VersionControlServer = this.Collection.GetService<VersionControlServer>();
            //this.WorkItemStore = this.Collection.GetService<WorkItemStore>();
            this.Connected = true;


        }

        public List<string> GetControllers()
        {
            if (!this.Connected)
                return null;
            IBuildControllerSpec buildControllerSpec = this.BuildServer.CreateBuildControllerSpec("*", "");
            IBuildControllerQueryResult buildControllerQueryResult = this.BuildServer.QueryBuildControllers(buildControllerSpec);
            return buildControllerQueryResult.Controllers.Select(a => a.Name).ToList<string>();
        }

        public DataSet GetQueuedBuilds(string reqController)
        {
            if (!this.Connected)
                return null;
            DataSet result = new DataSet("TFSBuildMonitor");
            DataTable dt = result.Tables.Add("Queue");
            dt.Columns.Add("Status", typeof(string));
            dt.Columns.Add("Definition", typeof(string));
            dt.Columns.Add("Reason", typeof(string));
            dt.Columns.Add("Priority", typeof(string));
            dt.Columns.Add("Triggered", typeof(DateTime));
            dt.Columns.Add("Position", typeof(int));
            dt.Columns.Add("Requester", typeof(string));
            dt.Columns.Add("AgentSettings", typeof(string));
            dt.TableName = "Queue";

            IQueuedBuildSpec queuedBuildSpec = this.BuildServer.CreateBuildQueueSpec("*", "*");
            IQueuedBuildQueryResult queuedBuildQueryResult = this.BuildServer.QueryQueuedBuilds(queuedBuildSpec);
            foreach (IQueuedBuild queuedBuild in queuedBuildQueryResult.QueuedBuilds)
            {
                string controller = queuedBuild.BuildController.Name;
                if (!reqController.Equals(controller))
                    continue;
                string agentSettings = GetAgentString(queuedBuild.BuildDefinition.ProcessParameters);
                string status = queuedBuild.Status.ToString();
                string definition = string.Format("{0}\\{1}", queuedBuild.TeamProject, queuedBuild.BuildDefinition.Name);
                string priority = queuedBuild.Priority.ToString();
                string reason = queuedBuild.Reason.ToString();
                DateTime triggered = queuedBuild.QueueTime;
                int position = queuedBuild.QueuePosition;
                string requester = queuedBuild.RequestedBy;
                if (queuedBuild.RequestedBy != queuedBuild.RequestedFor)
                    requester = string.Format("{0} (for {1})", requester, queuedBuild.RequestedFor);
                dt.Rows.Add(status, definition, reason, priority, triggered, position, requester, agentSettings);
            }
            return result;
        }

        private string GetAgentString(string xml)
        {
            XNamespace aw = "clr-namespace:Microsoft.TeamFoundation.Build.Workflow.Activities;assembly=Microsoft.TeamFoundation.Build.Workflow";
            XDocument xmlDoc = XDocument.Parse(xml);
            XElement agentSettings = xmlDoc.Descendants(aw + "AgentSettings").FirstOrDefault();
            if (agentSettings != null)
            {
                return string.Join(";", (agentSettings.Attributes().Where(a => a.Value.Contains("AgentSettings") != true).Select(b => b.Name + "=" + b.Value).ToArray()));
            }
            else
                return "";
        }


        public DataSet GetFinishedBuilds(string reqController, int daysCount)
        {
            if (!this.Connected)
                return null;
            DataSet result = new DataSet("TFSBuildMonitor");
            DataTable dt = result.Tables.Add("Finished");
            dt.Columns.Add("BuildNumber", typeof(string));
            dt.Columns.Add("Definition", typeof(string));
            dt.Columns.Add("Status", typeof(string));
            dt.Columns.Add("Started", typeof(DateTime));
            dt.Columns.Add("Ended", typeof(DateTime));
            dt.Columns.Add("Duration", typeof(double));
            dt.TableName = "Finished";

            IBuildDetailSpec buildDetailSpec = this.BuildServer.CreateBuildDetailSpec("*", "*");
            buildDetailSpec.InformationTypes = null; //it makes the load quicker
            buildDetailSpec.MinFinishTime = DateTime.Now.AddDays(daysCount);
            IBuildDetail[] buildDetails = this.BuildServer.QueryBuilds(buildDetailSpec).Builds;
            foreach (IBuildDetail buildDetail in buildDetails)
            {
                string controller = "";
                if (buildDetail.BuildController != null)
                    controller = buildDetail.BuildController.Name;
                if (!reqController.Equals(controller))
                    continue;
                string buildNumber = buildDetail.BuildNumber;
                string definition = string.Format("{0}\\{1}", buildDetail.TeamProject, buildDetail.BuildDefinition.Name);
                DateTime started = buildDetail.StartTime;
                DateTime ended = buildDetail.FinishTime;
                string status = buildDetail.Status.ToString();
                if (started == DateTime.MinValue || ended == DateTime.MinValue)
                    continue;
                dt.Rows.Add(buildNumber, definition, status, started, ended, (ended - started).TotalMinutes);
            }
            return result;
        }

        public DataSet GetTotalBuilds(string reqController, int daysCount)
        {
            if (!this.Connected)
                return null;
            DataSet result = new DataSet("TFSBuildMonitor");
            DataTable dt = result.Tables.Add("Total");
            dt.Columns.Add("Project", typeof(string));
            dt.Columns.Add("Count", typeof(int));
            dt.Columns.Add("Duration", typeof(double));
            dt.Columns.Add("Avg", typeof(double));
            dt.TableName = "Total";

            IBuildDetailSpec buildDetailSpec = this.BuildServer.CreateBuildDetailSpec("*", "*");
            buildDetailSpec.InformationTypes = null;
            buildDetailSpec.MinFinishTime = DateTime.Now.AddDays(daysCount);
            IBuildDetail[] buildDetails = this.BuildServer.QueryBuilds(buildDetailSpec).Builds;
            var queryHighScoreGroupsw = buildDetails.Where(w => w.BuildController != null && w.BuildController.Name.Equals(reqController) && w.StartTime != DateTime.MinValue && w.FinishTime != DateTime.MinValue);
            var queryHighScoreGroups = queryHighScoreGroupsw.GroupBy(g => g.TeamProject)
                .Select(b =>
                    new
                    {
                        Project = b.Key,
                        Count = b.Count(),
                        Duration = b.Sum(date => (date.FinishTime - date.StartTime).TotalHours),
                        Avg = b.Sum(date => (date.FinishTime - date.StartTime).TotalHours) / b.Count()
                    });
            foreach (var build in queryHighScoreGroups)
                dt.Rows.Add(build.Project, build.Count, build.Duration, build.Avg);

            return result;
        }

        public DataSet GetLastCheckIns(string reqController)
        {
            if (!this.Connected)
                return null;
            DataSet result = new DataSet("TFSBuildMonitor");
            DataTable dt = result.Tables.Add("LastCheckIns");
            dt.Columns.Add("Project", typeof(string));
            dt.Columns.Add("LastCheckIn", typeof(DateTime));
            dt.TableName = "LastCheckIns";

            var teamProjects = this.VersionControlServer.GetAllTeamProjects(false);

            foreach (var teamProject in teamProjects)
            {
                var spec = new ItemSpec(teamProject.ServerItem, RecursionType.Full);
                var history = this.VersionControlServer.QueryHistory(spec, 1);
                var date = history.First().CreationDate;

                dt.Rows.Add(teamProject.Name, date);
            }

            return result;
        }

        public DataSet GetAllBuilds(string reqController)
        {
            if (!this.Connected)
                return null;
            DataSet result = new DataSet("TFSBuildMonitor");
            DataTable dt = result.Tables.Add("All");
            dt.Columns.Add("TeamProject", typeof(string));
            dt.Columns.Add("BuildNumber", typeof(string));
            dt.Columns.Add("Status", typeof(string));
            dt.Columns.Add("DropLocationRoot", typeof(string));
            dt.Columns.Add("DropLocation", typeof(string));
            dt.TableName = "All";

            IBuildDetailSpec buildDetailSpec = this.BuildServer.CreateBuildDetailSpec("*", "*");
            buildDetailSpec.InformationTypes = null;

            IBuildDetail[] buildDetails = this.BuildServer.QueryBuilds(buildDetailSpec).Builds;

            foreach (var build in buildDetails)
            {
                string controller = "";
                if (build.BuildController != null)
                    controller = build.BuildController.Name;
                if (!reqController.Equals(controller))
                    continue;

                dt.Rows.Add(build.TeamProject, build.BuildNumber, build.Status.ToString(), build.DropLocationRoot,
                    build.DropLocation);
            }

            return result;
        }

        public DataSet GetAllProjectAdmins(string reqController)
        {
            if (!this.Connected)
                return null;
            DataSet result = new DataSet("TFSBuildMonitor");
            DataTable dt = result.Tables.Add("All");
            dt.Columns.Add("TeamProject", typeof(string));
            dt.Columns.Add("ProjectAdmins", typeof(string));
            dt.TableName = "All";

            var teamProjects = this.VersionControlServer.GetAllTeamProjects(false);
            var sec = Collection.GetService<IGroupSecurityService>();

            foreach (var teamProject in teamProjects)
            {
                Identity[] appGroups = sec.ListApplicationGroups(teamProject.ArtifactUri.AbsoluteUri).Where(g => g.AccountName.Equals("Project Administrators")).ToArray();
                string projectAdmins = string.Empty;

                foreach (Identity group in appGroups)
                {
                    Identity[] groupMembers = sec.ReadIdentities(SearchFactor.Sid, new string[] { group.Sid },
                        QueryMembership.Expanded);

                    foreach (Identity member in groupMembers)
                    {
                        if (member.Members != null)
                        {
                            foreach (string memberSid in member.Members)
                            {
                                Identity memberInfo = sec.ReadIdentity(SearchFactor.Sid, memberSid,
                                    QueryMembership.Expanded);

                                projectAdmins += memberInfo.AccountName + ";";
                            }
                        }
                    }
                }
                
                dt.Rows.Add(teamProject.Name, projectAdmins);
            }

            return result;
        }

    }
}
